/*/////////////////////////////////////////////////////////////////////////////////
/// An
///    ___   ____ ___ _____ ___  ____
///   / _ \ / ___|_ _|_   _/ _ \|  _ \
///  | | | | |  _ | |  | || | | | |_) |
///  | |_| | |_| || |  | || |_| |  _ <
///   \___/ \____|___| |_| \___/|_| \_\
///                              File
///
/// Copyright (c) 2008-2009 Ismail TARIM <ismail@royalspor.com> and the Ogitor Team
//
//This program is free software; you can redistribute it and/or modify it under
//the terms of the GNU Lesser General Public License as published by the Free Software
//Foundation; either version 2 of the License, or (at your option) any later
//version.
//
//This program is distributed in the hope that it will be useful, but WITHOUT
//ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
//FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public License along with
//this program; if not, write to the Free Software Foundation, Inc., 59 Temple
//Place - Suite 330, Boston, MA 02111-1307, USA, or go to
//http://www.gnu.org/copyleft/lesser.txt.
////////////////////////////////////////////////////////////////////////////////*/

#pragma once

namespace Ogitors
{

    //! Property assignation class
    /*!  
        Property assignation template class - internal use only!
    */
template <typename T>
class PropertySetterFunction
{
public:
    PropertySetterFunction()
        : object_ptr(0)
        , stub_ptr(0)
    {}

    template <class C, void (C::*TMethod)(const T&, bool)>
    static PropertySetterFunction from_method(C* object_ptr)
    {
        PropertySetterFunction d;
        d.object_ptr = object_ptr;
        d.stub_ptr = &method_stub<C, TMethod>; // #1

        return d;
    }

    void operator()(const T& value, bool display) const
    {
        return (*stub_ptr)(object_ptr, value, display);
    }

private:
    typedef void (*stub_type)(void* object_ptr, const T&, bool);

    void* object_ptr;
    stub_type stub_ptr;

    template <class C, void (C::*TMethod)(const T&, bool)>
    static void method_stub(void* object_ptr, const T& value, bool display)
    {
        C* p = static_cast<C*>(object_ptr);
        return (p->*TMethod)(value, display); // #2
    }
};

template <typename T>
class PropertyGetterFunction
{
public:
    PropertyGetterFunction()
        : object_ptr(0)
        , stub_ptr(0)
    {}

    template <class C, T (C::*TMethod)(void)>
    static PropertyGetterFunction from_method(C* object_ptr)
    {
        PropertyGetterFunction d;
        d.object_ptr = object_ptr;
        d.stub_ptr = &method_stub<C, TMethod>; // #1

        return d;
    }
	/*template <T, class C, T (C::*TMethod)(void)>
    static PropertyGetterFunction from_method(C* object_ptr)
    {
        PropertyGetterFunction d;
        d.object_ptr = object_ptr;
        d.stub_ptr = &method_stub<T, C, TMethod>; // #1
		
        return d;
    }*/

    T operator()(void) const
    {
        return (*stub_ptr)(object_ptr);
    }

private:
    typedef T (*stub_type)(void* object_ptr);

    void* object_ptr;
    stub_type stub_ptr;

    template <class C, T (C::*TMethod)(void)>
    static T method_stub(void* object_ptr)
    {
        C* p = static_cast<C*>(object_ptr);
        return (p->*TMethod)(); // #2
    }
};

class OgitorsSignalFunction
{
public:
    OgitorsSignalFunction()
        : object_ptr(0)
        , stub_ptr(0)
    {}

    template <class C, void (C::*TMethod)(Ogre::Any)>
    static OgitorsSignalFunction from_method(C* object_ptr)
    {
        OgitorsSignalFunction d;
        d.object_ptr = object_ptr;
        d.stub_ptr = &method_stub<C, TMethod>; // #1

        return d;
    }

    void operator()(Ogre::Any value) const
    {
        return (*stub_ptr)(object_ptr, value);
    }

private:
    typedef void (*stub_type)(void* object_ptr, Ogre::Any);

    void* object_ptr;
    stub_type stub_ptr;

    template <class C, void (C::*TMethod)(Ogre::Any)>
    static void method_stub(void* object_ptr, Ogre::Any value)
    {
        C* p = static_cast<C*>(object_ptr);
        return (p->*TMethod)(value); // #2
    }
};

namespace detail
{
struct connection_data
{
    OgitorsSignalFunction function;
    int ref_count;
    bool connected;
};
}

class OgitorsSignal;
class OgitorsScopedConnection;

class OgitorsConnection
{
    friend class OgitorsSignal;
    friend class OgitorsScopedConnection;
public:    
    OgitorsConnection() : mData(0) {}
    OgitorsConnection(const OgitorsConnection& c) : mData(c.mData && c.mData->ref_count > 0 ? c.mData : 0)
	{
		if(mData)
		{
			mData->ref_count++;
		}
	}

    virtual ~OgitorsConnection()
    {
        if(mData)
        {
            mData->ref_count--;
            if(mData->ref_count < 1)
                delete mData;
        }
    }

    OgitorsConnection& operator=(OgitorsConnection& c)
    {
        if(&c == this)
            return *this;

        if(c.mData != mData)
        {
            disconnect();
            mData = c.mData;
            mData->ref_count++;
        }
        return *this;
    }

    void _setData(detail::connection_data *data)
    {
        if(data != mData)
        {
            disconnect();
            mData = data;
            mData->ref_count++;
        }
    }

    inline bool connected()
    {
        return (mData && mData->connected);
    }

    void disconnect()
    {
       if(mData)
       {
           mData->ref_count--;
           if(mData->connected)
           {
               mData->connected = false;
           }
           else if(mData->ref_count < 1)
           {
               delete mData;
           }
           mData = 0;
       }
    };
protected:
    detail::connection_data *mData;
};

class OgitorsScopedConnection: public OgitorsConnection
{
public:
    OgitorsScopedConnection() {}

    virtual ~OgitorsScopedConnection()
    {
        disconnect();
    }

    OgitorsScopedConnection& operator=(OgitorsConnection& c)
    {
        if(&c == this)
            return *this;

        if(c.mData != mData)
        {
            disconnect();
            mData = c.mData;
            mData->ref_count++;
        }
        return *this;
    }

    OgitorsScopedConnection& operator=(OgitorsScopedConnection& c)
    {
        if(&c == this)
            return *this;

        if(c.mData != mData)
        {
            disconnect();
            mData = c.mData;
            mData->ref_count++;
        }
        return *this;
    }
};

class OgitorsSignal
{
public:
    typedef std::vector<detail::connection_data*> ConnectionVector;

    OgitorsSignal()
    {
    }

    ~OgitorsSignal()
    {
        for(unsigned int i = 0;i < mConnections.size();i++)    
        {
            mConnections[i]->connected = false;

            if(mConnections[i]->ref_count < 1)
            { 
                delete mConnections[i];
            }
        }
        mConnections.clear();
    }

    detail::connection_data *connect(const OgitorsSignalFunction& func)
    {
        detail::connection_data *data = new detail::connection_data;
        data->ref_count = 0;
        data->connected = true;
        data->function = func;
        mConnections.push_back(data);
        return data;
    }

    void invoke(const Ogre::Any &value)
    {
        for(int i = 0;i < (int)mConnections.size();i++)    
        {
            if(mConnections[i]->connected)
                 mConnections[i]->function(value);
            else 
            { 
                if(mConnections[i]->ref_count < 1)
                    delete mConnections[i];
                mConnections.erase(mConnections.begin() + i);
                --i;
            }
        }
    }

    void disconnect(OgitorsConnection *connection)
    {
        for(unsigned int i = 0;i < mConnections.size();i++)    
        {
            if(mConnections[i] == connection->mData)
            {
                mConnections[i]->connected = false;

                if(mConnections[i]->ref_count < 1)
                    delete mConnections[i];

                mConnections.erase(mConnections.begin() + i);
                break;
            }
        }
    };

protected:
    bool mConnected;
    ConnectionVector mConnections;
};

}